/*
 * 描          述:  <描述>
 * 修  改   人:  brady
 * 修改时间:  2012-12-11
 * <修改描述:>
 */
package com.tx.core.generator.model.impl;

import com.tx.core.generator.model.EntityProperty;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.generator.model.ValidateExpressionGenerator;
import com.tx.core.generator.model.ViewGenModel;
import com.tx.core.generator.util.JPAGeneratorUtils;
import io.swagger.v3.oas.annotations.Hidden;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ClassUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 控制层生成模型<br/>
 * <功能详细描述>
 *
 * @author brady
 * @version [版本号, 2012-12-11]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class JPAViewGeneratorModel implements ViewGenModel {

    /** 所在包名 */
    private final String basePackage;

    /** 实体注释 */
    private final String entityComment;

    /** 实体类型name */
    private final String entityTypeName;

    /** 实体类型simpleName */
    private final String entityTypeSimpleName;

    /** jpa字段列表 */
    private final List<EntityProperty> propertyList;

    /** jpa字段列表 */
    private final List<EntityProperty> viewablePropertyList;

    /** 主键字段列表 */
    private final EntityProperty pkProperty;

    /** 是否具有基础数据属性 */
    private final boolean hasBasicDataProperty;

    /** 是否具有基础数据枚举属性 */
    private final boolean hasBasicDataEnumProperty;

    /** 是否有编码属性 */
    private EntityProperty codeProperty;

    /** 是否有是否有效的属性 */
    private EntityProperty validProperty;

    /** 是否有是否有效的属性 */
    private EntityProperty nameProperty;

    /** 父节点id对应的属性 */
    private EntityProperty parentIdProperty;

    /** 父节点id对应的属性 */
    private EntityProperty parentProperty;

    /** <默认构造函数> */
    public JPAViewGeneratorModel(Class<?> entityType,
                                 ValidateExpressionGenerator validateExpressionGenerator) {
        super();
        String basePath = ClassUtils.convertClassNameToResourcePath(
                entityType.getName()) + "/../..";
        basePath = org.springframework.util.StringUtils.cleanPath(basePath);
        this.basePackage = ClassUtils.convertResourcePathToClassName(basePath);

        this.entityTypeName = entityType.getName();
        this.entityTypeSimpleName = entityType.getSimpleName();
        this.entityComment = JPAGeneratorUtils.parseEntityComment(entityType);

        List<EntityProperty> jpaPropertyList = JPAGeneratorUtils
                .parseEntityPropertyList(entityType);
        this.propertyList = jpaPropertyList.stream().map(column -> {
            return column;
        }).collect(Collectors.toList());

        this.pkProperty = this.propertyList.stream().filter(column -> {
            return column.isPrimaryKey();
        }).collect(Collectors.toList()).get(0);
        AssertUtils.isTrue(this.pkProperty != null, "没有找到主键字段");

        this.hasBasicDataEnumProperty = jpaPropertyList.stream().map(c -> {
                    return (JPAEntityProperty) c;
                })
                .anyMatch(c -> c.getJpaColumnInfo().getPropertyType().isEnum()
                        && JPAGeneratorUtils.isBasicdataEnum(c.getJpaColumnInfo().getPropertyType()));
        this.hasBasicDataProperty = jpaPropertyList.stream()
                .anyMatch(c -> JPAGeneratorUtils.isBasicdataType(((JPAEntityProperty) c).getJpaColumnInfo().getPropertyType()));

        if (validateExpressionGenerator != null) {
            this.propertyList.stream().forEach(property -> {
                validateExpressionGenerator.generate(property, entityType);
            });
        }

        this.viewablePropertyList = jpaPropertyList.stream().map(c -> {
            return (JPAEntityProperty) c;
        }).filter(column -> {
            if (column.getJpaColumnInfo()
                    .getTypeDescriptor()
                    .hasAnnotation(Hidden.class)) {
                return false;
            }
            return true;
        }).collect(Collectors.toList());

        Set<String> propertyNameSet = new HashSet<>();
        jpaPropertyList.stream().map(c -> {
            return (JPAEntityProperty)c;
        }).forEach(property -> {
            propertyNameSet.add(property.getPropertyName());
            if (StringUtils.equals("code", property.getPropertyName())
                    && !property.isPrimaryKey()) {
                //如果主键就是code，则无需标定hasCodeProperty
                this.codeProperty = property;
                AssertUtils.isTrue(
                        String.class.isAssignableFrom(
                                property.getJpaColumnInfo().getPropertyType()),
                        "code type should is String.");
            } else if (StringUtils.equals("valid",
                    property.getPropertyName())) {
                this.validProperty = property;
                AssertUtils.isTrue(Boolean.class.isAssignableFrom(
                                property.getJpaColumnInfo().getPropertyType())
                                || boolean.class.equals(
                                property.getJpaColumnInfo().getPropertyType()),
                        "valid type should is boolean or Boolean.");
            } else if (StringUtils.equals("parentId",
                    property.getPropertyName())) {
                this.parentIdProperty = property;
                AssertUtils.isTrue(
                        ((JPAEntityProperty) this.pkProperty)
                                .getJpaColumnInfo()
                                .getPropertyType()
                                .equals(property.getJpaColumnInfo()
                                        .getPropertyType()),
                        "parentId type:{} should equals pk type:{}.",
                        new Object[]{
                                property.getJpaColumnInfo().getPropertyType(),
                                ((JPAEntityProperty) this.pkProperty)
                                        .getJpaColumnInfo()
                                        .getPropertyType()});
            } else if (StringUtils.equals("name", property.getPropertyName())) {
                this.nameProperty = property;
            } else if (StringUtils.equals("parent",
                    property.getPropertyName())) {
                this.parentProperty = property;
                AssertUtils.isTrue(
                        property.getJpaColumnInfo()
                                .getPropertyType()
                                .equals(entityType),
                        "parent type:{} should equals entity type:{}.",
                        new Object[]{
                                property.getJpaColumnInfo().getPropertyType(),
                                entityType});
            }
        });

        if (this.nameProperty == null) {
            this.nameProperty = this.codeProperty;
        }
        if (this.nameProperty == null) {
            this.nameProperty = this.pkProperty;
        }
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getParentProperty() {
        return parentProperty;
    }

    /**
     * @return
     */
    @Override
    public String getBasePackage() {
        return basePackage;
    }

    /**
     * @return
     */
    @Override
    public String getEntityComment() {
        return entityComment;
    }

    /**
     * @return
     */
    @Override
    public String getEntityTypeName() {
        return entityTypeName;
    }

    /**
     * @return
     */
    @Override
    public String getEntityTypeSimpleName() {
        return entityTypeSimpleName;
    }

    /**
     * @return
     */
    @Override
    public List<EntityProperty> getPropertyList() {
        return propertyList;
    }

    /**
     * @return
     */
    @Override
    public List<EntityProperty> getViewablePropertyList() {
        return viewablePropertyList;
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getPkProperty() {
        return pkProperty;
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getCodeProperty() {
        return codeProperty;
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getValidProperty() {
        return validProperty;
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getParentIdProperty() {
        return parentIdProperty;
    }

    /**
     * @return
     */
    @Override
    public EntityProperty getNameProperty() {
        return nameProperty;
    }

    /**
     * @return 返回 hasBasicDataProperty
     */
    @Override
    public boolean isHasBasicDataProperty() {
        return hasBasicDataProperty;
    }

    /**
     * @return 返回 hasBasicDataEnumProperty
     */
    @Override
    public boolean isHasBasicDataEnumProperty() {
        return hasBasicDataEnumProperty;
    }
}
